package com.whfc.emp.xxl.job;

import com.alibaba.fastjson.JSON;
import com.whfc.XxlJobConfig;
import com.whfc.common.face.szyc.QueryPerson;
import com.whfc.common.face.szyc.SearchPerson;
import com.whfc.common.face.szyc.SzycConst;
import com.whfc.common.util.CollectionUtil;
import com.whfc.common.util.DateUtil;
import com.whfc.emp.dao.*;
import com.whfc.emp.dto.AppEmpDTO;
import com.whfc.emp.dto.AppFaceGateConfigDTO;
import com.whfc.emp.dto.AppFaceGateEmpDTO;
import com.whfc.emp.entity.AppFaceGate;
import com.whfc.emp.entity.AppFaceGatePerson;
import com.whfc.emp.entity.AppFaceGateVisitor;
import com.whfc.emp.enums.FaceGateType;
import com.whfc.emp.enums.TaskType;
import com.whfc.emp.factory.AppFaceGateServiceFactory;
import com.whfc.emp.manager.FaceGateManager;
import com.whfc.emp.mqtt.MqttConfig;
import com.whfc.emp.param.AppFaceGateGrantEmpParam;
import com.whfc.emp.param.FaceGateGrantEmdAddParam;
import com.whfc.emp.param.FaceGateGrantEmdAuthParam;
import com.whfc.emp.param.FaceGateGrantEmdImgAddParam;
import com.whfc.emp.redis.MqttEmpRedisDao;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;

/**
 * @ClasssName FaceGateTask
 * @Description 闸机定时任务
 * @Author hw
 * @Date 2021/1/25 11:32
 * @Version 1.0
 */
@Component
@ConditionalOnBean(XxlJobConfig.class)
public class FaceGateJob {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AppFaceGateConfigMapper appFaceGateConfigMapper;

    @Autowired
    private AppFaceGateServiceFactory appFaceGateServiceFactory;

    @Autowired
    private AppFaceGatePersonMapper appFaceGatePersonMapper;

    @Autowired
    private AppFaceGateVisitorMapper appFaceGateVisitorMapper;

    @Autowired
    private AppFaceGateMapper appFaceGateMapper;

    @Autowired(required = false)
    private MqttConfig.MqttMessageSender mqttMessageSender;

    @Autowired
    private MqttEmpRedisDao empCodeRedisDao;

    @Autowired
    private AppEmpMapper appEmpMapper;


    @Value("${mqtt.enabled}")
    private Boolean mqttEnable;

    /**
     * token 有效期1天 每天凌晨3点刷新
     */
    @XxlJob("refreshAccessToken")
    public void refreshAccessToken() {
        List<AppFaceGateConfigDTO> list = appFaceGateConfigMapper.selectAll();
        for (AppFaceGateConfigDTO faceGateConfig : list) {
            try {
                XxlJobHelper.log("定时任务更新{}平台,id:{},token...", faceGateConfig.getName(), faceGateConfig.getAppId());
                FaceGateManager faceGateService = appFaceGateServiceFactory.getFaceGateServiceById(faceGateConfig.getConfigId());
                faceGateService.getToken();
            } catch (Exception ex) {
                XxlJobHelper.handleFail("定时任务更新" + faceGateConfig.getName() + "平台,id:" + faceGateConfig.getAppId() + ",error:" + ex.getMessage());
            }
        }
    }

    /**
     * 添加闸机人员信息（每分钟一次）
     */
    @XxlJob("faceGateGrantEmd")
    public void faceGateGrantEmd() {
        List<Integer> taskTypeList = new ArrayList<>();
        taskTypeList.add(TaskType.NOT_START.getValue());
        taskTypeList.add(TaskType.EMP_ADD.getValue());
        taskTypeList.add(TaskType.EMP_IMG_ADD.getValue());
        List<AppFaceGateEmpDTO> list = appFaceGatePersonMapper.selectByTaskType(taskTypeList);
        if (list.isEmpty()) {
            //没有可执行的闸机人员
            XxlJobHelper.handleSuccess("没有可执行的闸机人员");
            return;
        }
        list.forEach(appFaceGateEmpDTO -> {
            try {
                faceGateGrantEmd(appFaceGateEmpDTO);
            } catch (Exception e) {
                XxlJobHelper.handleFail("定时执行人员empid:" + appFaceGateEmpDTO.getEmpId() + ",taskType:" + appFaceGateEmpDTO.getTaskType() + ",error:" + e.getMessage());
            }
        });
    }

    /**
     * 取消闸机人员授权
     */
    @XxlJob("faceGateRevokeForUnAuth")
    public void faceGateRevokeForUnAuth() {
        List<AppFaceGateEmpDTO> list = appFaceGatePersonMapper.selectByTaskType(Collections.singletonList(TaskType.EMP_UN_AUTH.getValue()));
        if (list.isEmpty()) {
            //没有可执行的闸机人员
            XxlJobHelper.handleSuccess("没有可执行的取消授权闸机人员");
            return;
        }
        Map<Integer, List<AppFaceGateEmpDTO>> faceGatePersonMap = CollectionUtil.groupBy(list, AppFaceGateEmpDTO::getFaceGateId);
        for (Map.Entry<Integer, List<AppFaceGateEmpDTO>> faceEntry : faceGatePersonMap.entrySet()) {
            try {
                Integer faceGateId = faceEntry.getKey();
                List<Integer> needHandleEmpIdList = faceEntry.getValue().stream().map(AppFaceGateEmpDTO::getEmpId).collect(Collectors.toList());
                logger.info("取消闸机人员授权,faceGateId:{},num:{}", faceGateId, needHandleEmpIdList.size());
                XxlJobHelper.log("取消闸机人员授权,faceGateId:{},num:{}", faceGateId, needHandleEmpIdList.size());
                faceGateRevokeEmp(faceGateId, needHandleEmpIdList);
            } catch (Exception ex) {
                logger.warn("取消闸机人员授权.faceGateId=" + faceEntry.getKey(), ex);
                XxlJobHelper.handleFail("取消闸机人员授权失败.faceGateId=" + faceEntry.getKey());
            }
        }
    }

    /**
     * 访客-过期后,取消闸机授权
     */
    @XxlJob("faceGateRevokeForVisitor")
    public void faceGateRevokeForVisitor() {
        List<AppFaceGateVisitor> list = appFaceGateVisitorMapper.selectOverdue(DateUtil.formatDate(new Date(), DateUtil.DATE_FORMAT));
        if (list.isEmpty()) {
            //没有可执行的闸机人员
            XxlJobHelper.handleSuccess("没有需要执行的临时人员");
            return;
        }
        list.forEach(appFaceGateVisitor -> {
            AppFaceGate appFaceGate = appFaceGateMapper.selectByPrimaryKey(appFaceGateVisitor.getFaceGateId());
            if (appFaceGate == null) {
                XxlJobHelper.handleSuccess("对应闸机不存在");
            }
            FaceGateManager faceGateService = appFaceGateServiceFactory.getFaceGateService(appFaceGateVisitor.getFaceGateId());
            faceGateService.deviceAuthorizationCancel(appFaceGate.getDeviceKey(), appFaceGateVisitor.getRemark());
        });
    }

    /**
     * 已删除人员- 取消闸机授权
     */
    @XxlJob("faceGateRevokeForDeleteEmp")
    public void faceGateRevokeForDeleteEmp() {
        //查询所有已删除人员
        List<AppEmpDTO> allDelEmpList = appEmpMapper.selectDelEmpList();
        //按项目分组
        Map<Integer, List<AppEmpDTO>> empMap = CollectionUtil.groupBy(allDelEmpList, AppEmpDTO::getDeptId);
        if (!empMap.isEmpty()) {
            for (Map.Entry<Integer, List<AppEmpDTO>> empEntry : empMap.entrySet()) {
                Integer deptId = empEntry.getKey();
                List<Integer> empIdList = empEntry.getValue().stream().map(AppEmpDTO::getEmpId).collect(Collectors.toList());
                List<AppFaceGatePerson> faceGatePersonList = appFaceGatePersonMapper.selectByEmpIdList(empIdList);
                logger.info("项目删除人员,消闸机授权,deptId:{},outerNum:{},num:{}", deptId, empIdList.size(), faceGatePersonList.size());
                Map<Integer, List<AppFaceGatePerson>> faceGatePersonMap = CollectionUtil.groupBy(faceGatePersonList, AppFaceGatePerson::getFaceGateId);
                for (Map.Entry<Integer, List<AppFaceGatePerson>> faceEntry : faceGatePersonMap.entrySet()) {
                    try {
                        Integer faceGateId = faceEntry.getKey();
                        List<Integer> needHandleEmpIdList = faceEntry.getValue().stream().map(AppFaceGatePerson::getEmpId).collect(Collectors.toList());
                        logger.info("项目删除人员,消闸机授权,deptId:{},faceGateId:{},num:{}", deptId, faceGateId, needHandleEmpIdList.size());
                        XxlJobHelper.log("项目删除人员,消闸机授权,deptId:{},faceGateId:{},num:{}", deptId, faceGateId, needHandleEmpIdList.size());
                        faceGateRevokeEmp(faceGateId, needHandleEmpIdList);
                    } catch (Exception ex) {
                        logger.warn("项目删除人员,消闸机授权失败.faceGateId=" + faceEntry.getKey(), ex);
                        XxlJobHelper.handleFail("项目删除人员,消闸机授权失败.faceGateId=" + faceEntry.getKey());
                    }
                }
            }
        }
    }


    /**
     * 已离职人员- 取消闸机授权
     */
    @XxlJob("faceGateRevokeForOuterEmp")
    public void faceGateRevokeForOuterEmp() {
        //查询所有已离职人员
        List<AppEmpDTO> allOuterEmpList = appEmpMapper.selectOuterEmpList();
        //按项目分组
        Map<Integer, List<AppEmpDTO>> empMap = CollectionUtil.groupBy(allOuterEmpList, AppEmpDTO::getDeptId);
        if (!empMap.isEmpty()) {
            for (Map.Entry<Integer, List<AppEmpDTO>> empEntry : empMap.entrySet()) {
                Integer deptId = empEntry.getKey();
                List<Integer> empIdList = empEntry.getValue().stream().map(AppEmpDTO::getEmpId).collect(Collectors.toList());
                List<AppFaceGatePerson> faceGatePersonList = appFaceGatePersonMapper.selectByEmpIdList(empIdList);
                logger.info("项目离职人员,消闸机授权,deptId:{},outerNum:{},num:{}", deptId, empIdList.size(), faceGatePersonList.size());
                Map<Integer, List<AppFaceGatePerson>> faceGatePersonMap = CollectionUtil.groupBy(faceGatePersonList, AppFaceGatePerson::getFaceGateId);
                for (Map.Entry<Integer, List<AppFaceGatePerson>> faceEntry : faceGatePersonMap.entrySet()) {
                    try {
                        Integer faceGateId = faceEntry.getKey();
                        List<Integer> needHandleEmpIdList = faceEntry.getValue().stream().map(AppFaceGatePerson::getEmpId).collect(Collectors.toList());
                        logger.info("项目离职人员,消闸机授权,deptId:{},faceGateId:{},num:{}", deptId, faceGateId, needHandleEmpIdList.size());
                        XxlJobHelper.log("项目离职人员,消闸机授权,deptId:{},faceGateId:{},num:{}", deptId, faceGateId, needHandleEmpIdList.size());
                        faceGateRevokeEmp(faceGateId, needHandleEmpIdList);
                    } catch (Exception ex) {
                        logger.warn("项目离职人员,消闸机授权失败.faceGateId=" + faceEntry.getKey(), ex);
                        XxlJobHelper.handleFail("项目离职人员,消闸机授权失败.faceGateId=" + faceEntry.getKey());
                    }
                }
            }
        }
    }

    /**
     * 刷新深圳玉川 MQTT版人员同步
     */
    @XxlJob("refreshSzycMqttEmp")
    public void refreshSzycMqttEmp() {
        if (mqttEnable == null || !mqttEnable) {
            return;
        }
        //获取所有深圳玉川 MQTT 设备
        List<AppFaceGate> list = appFaceGateMapper.selectByPlatform(FaceGateType.MQTT_HQSX_ZJJ.getCode());
        if (list.isEmpty()) {
            return;
        }
        for (AppFaceGate appFaceGate : list) {
            String topic = MessageFormat.format(SzycConst.TOPIC_REQ, appFaceGate.getDeviceKey());
            String messageId = Long.toString(System.currentTimeMillis());
            QueryPerson queryPerson = new QueryPerson(messageId);
            mqttMessageSender.sendToMqtt(topic, JSON.toJSONString(queryPerson));
        }
        // 查询所有人员后暂停5分钟再执行刷新人员信息任务
        LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(5));
        // 刷新深圳MQTT人员信息
        refreshSzycMqttEmpInfo();
    }

    /**
     * 刷新深圳玉川 MQTT版人员信息同步
     */
    private void refreshSzycMqttEmpInfo() {
        logger.info("MQTT发送查询人员信息");
        if (MqttConfig.MQTT_THREAD != null) {
            return;
        }
        //获取队列长度
        Integer size = empCodeRedisDao.getMqttTopicSize();
        if (size == null || size < 1) {
            return;
        }
        for (int i = 0; i < size; i++) {
            SearchPerson searchPerson;
            try {
                searchPerson = empCodeRedisDao.getMqttTopic();
                if (searchPerson == null) {
                    continue;
                }
                logger.info("MQTT发送查询人员信息,topic:{},customId:{}", searchPerson.getTopic(), searchPerson.getInfo().getCustomId());
                mqttMessageSender.sendToMqtt(searchPerson.getTopic(), JSON.toJSONString(searchPerson));
                // 设置当前线程
                MqttConfig.MQTT_THREAD = Thread.currentThread();
                // 当前查询数据 如果不为空 代表数据未传输到MQTT 需要将数据重新保存到缓存中
                if (MqttConfig.SEARCH_PERSON != null) {
                    empCodeRedisDao.saveMqttTopic(MqttConfig.SEARCH_PERSON);
                }
                MqttConfig.SEARCH_PERSON = searchPerson;
            } catch (Exception e) {
                logger.warn("MQTT发送查询人员信息失败", e);
                continue;
            }
            // 一分钟如果没响应自动开锁
            LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
        }
        MqttConfig.MQTT_THREAD = null;
    }

    /**
     * 使用taskType执行对应任务
     *
     * @param appFaceGateEmpDTO
     */
    private void faceGateGrantEmd(AppFaceGateEmpDTO appFaceGateEmpDTO) {
        Integer taskType = appFaceGateEmpDTO.getTaskType();
        switch (taskType) {
            case 0:
                faceGateGrantEmdAdd(appFaceGateEmpDTO);
                break;
            case 1:
                faceGateGrantEmdImgAdd(appFaceGateEmpDTO);
                break;
            case 2:
                faceGateGrantEmdAuth(appFaceGateEmpDTO);
                break;
            default:
                break;
        }
    }

    /**
     * 闸机人员取消授权
     *
     * @param faceGateId 闸机ID
     * @param empIdList  取消授权人员ID
     */
    private void faceGateRevokeEmp(Integer faceGateId, List<Integer> empIdList) {
        FaceGateManager faceGateManager = appFaceGateServiceFactory.getFaceGateService(faceGateId);
        if (faceGateManager != null) {
            AppFaceGateGrantEmpParam param = new AppFaceGateGrantEmpParam();
            param.setFaceGateId(faceGateId);
            param.setEmpIdList(empIdList);
            faceGateManager.faceGateRevokeEmp(param);
        }
    }

    /**
     * 闸机人员注册
     *
     * @param request
     */
    private void faceGateGrantEmdAdd(AppFaceGateEmpDTO request) {
        Integer empId = request.getEmpId();
        String deviceKey = request.getDeviceKey();
        Integer personId = request.getPersonId();
        Integer faceGateId = request.getFaceGateId();
        FaceGateGrantEmdAddParam param = new FaceGateGrantEmdAddParam();
        param.setDeviceKey(deviceKey);
        param.setEmpId(empId);
        param.setPersonId(personId);
        param.setFaceGateId(faceGateId);
        FaceGateManager faceGateService = appFaceGateServiceFactory.getFaceGateService(param.getDeviceKey());
        faceGateService.faceGateGrantEmdAdd(param);
    }

    /**
     * 闸机人员照片注册
     *
     * @param request
     */
    private void faceGateGrantEmdImgAdd(AppFaceGateEmpDTO request) {
        String imgUrl = request.getAvatar();
        String personGuid = request.getPersonGuid();
        String deviceKey = request.getDeviceKey();
        Integer personId = request.getPersonId();
        FaceGateGrantEmdImgAddParam param = new FaceGateGrantEmdImgAddParam();
        param.setDeviceKey(deviceKey);
        param.setPersonId(personId);
        param.setImgUrl(imgUrl);
        param.setPersonGuid(personGuid);
        FaceGateManager faceGateService = appFaceGateServiceFactory.getFaceGateService(param.getDeviceKey());
        faceGateService.faceGateGrantEmdImgAdd(param);
    }

    /**
     * 闸机人员授权
     *
     * @param request
     */
    private void faceGateGrantEmdAuth(AppFaceGateEmpDTO request) {
        String personGuid = request.getPersonGuid();
        String deviceKey = request.getDeviceKey();
        Integer personId = request.getPersonId();
        FaceGateGrantEmdAuthParam param = new FaceGateGrantEmdAuthParam();
        param.setDeviceKey(deviceKey);
        param.setPersonGuid(personGuid);
        param.setPersonId(personId);
        FaceGateManager faceGateService = appFaceGateServiceFactory.getFaceGateService(param.getDeviceKey());
        faceGateService.faceGateGrantEmdAuth(param);
    }

}
